
    # Filter tables Generator for Forbin LFO

class FIRFilterDesign

    #--------------------------------------------------------------------------
    # Description: constants -- filter window types

    HAMMING     = 2314
    RECTANGULAR = 2371
    HANNING     = 9121
    BLACKMAN    = 8171

    #--------------------------------------------------------------------------
    # Description: constants -- filter type

    HIGH_PASS = 771
    LOW_PASS  = 716
    BAND_PASS = 891

    #--------------------------------------------------------------------------
    # Description: constructor
    #  Parameters: the sample rate
    #              filter order -- controls the sharpness of the filter
    #              filter type -- high/low or bandpass
    #              window type -- controls the amount of ripple in the response
    #      Return: nothing
    #  Exceptions: nothing

    def initialize(sample_rate, filter_order, filter_type = LOW_PASS, window_type = HANNING)
        @m_niquist_rate = sample_rate.to_f() * 0.5
        @m_type         = filter_type
        @m_window_type  = window_type
        @m_order        = filter_order
    end

    #--------------------------------------------------------------------------
    # Description: generate the tables
    #  Parameters: none
    #      Return: none
    #  Exceptions: nothing

    def genTables()
        @m_window_type = RECTANGULAR
        dispWindow(getWindow(), "rectangular")
        @m_window_type = HANNING
        dispWindow(getWindow(), "hanning")
        @m_window_type = HAMMING
        dispWindow(getWindow(), "hamming")
        @m_window_type = BLACKMAN
        dispWindow(getWindow(), "blackman")
    end

    #--------------------------------------------------------------------------
    # Description: generate the tables
    #  Parameters: none
    #      Return: none
    #  Exceptions: nothing

    def dispWindow(ta, name)
        wix = getWindow()
        printf(";\n%s_window:\n", name)
        rt = 0.0
        for idx in 1...wix.length()
            av = (wix[idx] * 0xffff + 0.5).to_i()
            printf("     .short %d\n", av)
            rt += wix[idx]
        end
        printf("; %8.5f\n", rt)
    end

    #--------------------------------------------------------------------------
    # Description: set/get the frequency of the turnover point of the filter
    #  Parameters: the frequency of the turn over point for LP/HP and low part
    #              of BP
    #      Return: turn over point and second one if necessary
    #  Exceptions: nothing

    def frequency(f1 = nil, f2 = nil)
        @m_freq = f1      if (f1)
        @m_freq_high = f2 if (f2)
        if (@m_freq_high)
            if (@m_freq > @m_freq_high)
                temp         = @m_freq
                @m_freq      = @m_freq_high
                @m_freq_high = temp
            end
        end
        return @m_freq, @m_freq_high if (@m_freq_high)
        return @m_freq
    end

    #--------------------------------------------------------------------------
    # Description: set/get the frequency of the turnover point of the filter
    #  Parameters: the frequency of the turn over point for LP/HP and low part
    #              of BP
    #      Return: turn over point and second one if necessary
    #  Exceptions: nothing

    def sampleFrequency(sfreq = nil)
        @m_niquest_rate = freq * 0.5 if (sfreq)
        return @m_niquest_rate * 2.0
    end

    #--------------------------------------------------------------------------
    # Description: transition bandwidth
    #  Parameters: none
    #      Return: transition bandwidth
    #  Exceptions: nothing

    def transitionBandwidth()
        if (@m_window_type == RECTANGULAR)
            return 1.84 * @m_niquist_rate / @m_order
        elsif (@m_window_type == HANNING)
            return 6.22 * @m_niquist_rate / @m_order
        elsif (@m_window_type == HAMMING)
            return 6.64 * @m_niquist_rate / @m_order
        elsif (@m_window_type == BLACKMAN)
            return 11.13 * @m_niquist_rate / @m_order
        else
            raise 'unknown window type'
        end
    end

    #--------------------------------------------------------------------------
    # Description: design the filter given all the info
    #  Parameters: none
    #      Return: array of filter coefficients
    #  Exceptions: nothing

    def design()

        # based upon the filter type and the frequencies work out some stuff...

        if (@m_type == LOW_PASS)
            w0 = 0.0
            w1 = Math::PI * (@m_freq + 0.5 * transitionBandwidth()) / @m_niquist_rate
        elsif (@m_type == HIGH_PASS)
            w0 = Math::PI
            w1 = Math::PI * (1.0 - (@m_freq - 0.5 * transitionBandwidth()) / @m_niquist_rate)
        elsif (@m_type == BAND_PASS)

            w0 = 0.5 * Math::PI * (@m_freq_high + @m_freq                        ) / @m_niquist_rate
            w1 = 0.5 * Math::PI * (@m_freq_high - @m_freq + transitionBandwidth()) / @m_niquist_rate

        else
            raise 'unknown filter type'
        end

        # get the windowing function we have decided to use

        window = getWindow()

        # filter coefficients (NB not normalised to unit maximum gain)

        coeff    = Array.new(@m_order + 1)
        coeff[0] = w1 / Math::PI
        m    = (@m_order / 2).to_i()
)        for n in 1..m
            coeff[n] = Math.sin(n.to_f() * w1) * Math.cos(n.to_f() * w0) * window[n] / (n.to_f() * Math::PI)
        end

        # shift impulse response to make filter causal

        for n in (m + 1)..@m_order
            coeff[n] = coeff[n - m]
        end
        for n in 0..(m - 1)
            coeff[n] = coeff[@m_order - n]
        end
        coeff[m] = w1 / Math::PI

        return coeff
    end

    #--------------------------------------------------------------------------

  private

    #--------------------------------------------------------------------------
    # Description: get the attenuation coeficient based upon the window type
    #  Parameters: the window type
    #      Return: attenuation coefficient
    #  Exceptions: nothing

    def attenuation(wt)
        return 21.0 if (wt == RECTANGULAR)
        return 44.0 if (wt == HANNING)
        return 53.0 if (wt == HAMMING)
        return 74.0 if (wt == BLACKMAN)
        return 999.0
    end

    #--------------------------------------------------------------------------
    # Description: return an array of windowing coefficients based upon the
    #              windowing type
    #  Parameters: none
    #      Return: array of window coefficients
    #  Exceptions: nothing

    def getWindow()
        m      = (@m_order / 2).to_i()
        window = Array.new(m + 1)
        window[0] = 0.0
        r      = Math::PI / (m + 1).to_f()
        if (@m_window_type == RECTANGULAR)
            for n in 1..m
               window[n] = 1.0
            end
        elsif (@m_window_type == HANNING)
            for n in 1..m
               window[n] = 0.5 + 0.5 * Math.cos(n.to_f() * r)
            end
        elsif (@m_window_type == HAMMING)
            for n in 1..m
               window[n] = 0.54 + 0.46 * Math.cos(n.to_f() * r)
            end
        elsif (@m_window_type == BLACKMAN)
            for n in 1..m
               window[n] = 0.42 + 0.5  * Math.cos(      n.to_f() * r) +
                                  0.08 * Math.cos(2.0 * n.to_f() * r)
            end
        else
            raise 'unknown window type'
        end
        return window
    end

    #--------------------------------------------------------------------------

end

FIRFilterDesign.new(10000, 32).genTables()
